// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <cstdint>
#include <sstream>
#include <string>

#include <boost/system/error_code.hpp>

namespace coin2::base::request::http2 {

//
// enum Nghttp2ErrorCode - copied from library nghttp2
//
enum Nghttp2ErrorCode {
  /**
   * No errors.
   */
  NGHTTP2_NO_ERROR = 0x00,
  /**
   * PROTOCOL_ERROR
   */
  NGHTTP2_PROTOCOL_ERROR = 0x01,
  /**
   * INTERNAL_ERROR
   */
  NGHTTP2_INTERNAL_ERROR = 0x02,
  /**
   * FLOW_CONTROL_ERROR
   */
  NGHTTP2_FLOW_CONTROL_ERROR = 0x03,
  /**
   * SETTINGS_TIMEOUT
   */
  NGHTTP2_SETTINGS_TIMEOUT = 0x04,
  /**
   * STREAM_CLOSED
   */
  NGHTTP2_STREAM_CLOSED = 0x05,
  /**
   * FRAME_SIZE_ERROR
   */
  NGHTTP2_FRAME_SIZE_ERROR = 0x06,
  /**
   * REFUSED_STREAM
   */
  NGHTTP2_REFUSED_STREAM = 0x07,
  /**
   * CANCEL
   */
  NGHTTP2_CANCEL = 0x08,
  /**
   * COMPRESSION_ERROR
   */
  NGHTTP2_COMPRESSION_ERROR = 0x09,
  /**
   * CONNECT_ERROR
   */
  NGHTTP2_CONNECT_ERROR = 0x0a,
  /**
   * ENHANCE_YOUR_CALM
   */
  NGHTTP2_ENHANCE_YOUR_CALM = 0x0b,
  /**
   * INADEQUATE_SECURITY
   */
  NGHTTP2_INADEQUATE_SECURITY = 0x0c,
  /**
   * HTTP_1_1_REQUIRED
   */
  NGHTTP2_HTTP_1_1_REQUIRED = 0x0d
};

class Nghttp2ErrorFormater {
 public:
  // convert nghttp2 error code to error string
  static auto GetErrorString(int32_t error_code) -> std::string {
    std::stringstream ss;
    auto ec = static_cast<Nghttp2ErrorCode>(error_code);
    switch (ec) {
      case NGHTTP2_NO_ERROR:
        ss << error_code << " - NGHTTP2_NO_ERROR";
        break;
      case NGHTTP2_PROTOCOL_ERROR:
        ss << error_code << " - NGHTTP2_PROTOCOL_ERROR";
        break;
      case NGHTTP2_INTERNAL_ERROR:
        ss << error_code << " - NGHTTP2_INTERNAL_ERROR";
        break;
      case NGHTTP2_FLOW_CONTROL_ERROR:
        ss << error_code << " - NGHTTP2_FLOW_CONTROL_ERROR";
        break;
      case NGHTTP2_SETTINGS_TIMEOUT:
        ss << error_code << " - NGHTTP2_SETTINGS_TIMEOUT";
        break;
      case NGHTTP2_STREAM_CLOSED:
        ss << error_code << " - NGHTTP2_STREAM_CLOSED";
        break;
      case NGHTTP2_FRAME_SIZE_ERROR:
        ss << error_code << " - NGHTTP2_FRAME_SIZE_ERROR";
        break;
      case NGHTTP2_REFUSED_STREAM:
        ss << error_code << " - NGHTTP2_REFUSED_STREAM";
        break;
      case NGHTTP2_CANCEL:
        ss << error_code << " - NGHTTP2_CANCEL";
        break;
      case NGHTTP2_COMPRESSION_ERROR:
        ss << error_code << " - NGHTTP2_COMPRESSION_ERROR";
        break;
      case NGHTTP2_CONNECT_ERROR:
        ss << error_code << " - NGHTTP2_CONNECT_ERROR";
        break;
      case NGHTTP2_ENHANCE_YOUR_CALM:
        ss << error_code << " - NGHTTP2_ENHANCE_YOUR_CALM";
        break;
      case NGHTTP2_INADEQUATE_SECURITY:
        ss << error_code << " - NGHTTP2_INADEQUATE_SECURITY";
        break;
      case NGHTTP2_HTTP_1_1_REQUIRED:
        ss << error_code << " - NGHTTP2_HTTP_1_1_REQUIRED";
        break;
      default:
        ss << error_code << " - UNKNOWN ERROR";
        break;
    }

    return ss.str();
  }

  // convert nghttp2 error code to boost error
  static auto GetBoostError(int32_t error_code) -> boost::system::error_code {
    // TODO(daniel) - handle error codes
    switch (error_code) {
      case 0:
        return boost::system::errc::make_error_code(boost::system::errc::success);
      case 2:
      case 8:
        return boost::system::errc::make_error_code(boost::system::errc::operation_canceled);
      case 10:
        return boost::system::errc::make_error_code(boost::system::errc::not_connected);
      default:
        return boost::system::errc::make_error_code(boost::system::errc::timed_out);
    }
  }
};

}  // namespace coin2::base::request::http2
